//Copyright 2008 Kristian Gundry And Rune Juhl-Petersen
//This file is part of Plain.

//Plain is free software: you can redistribute it and/or modify
//it under the terms of the GNU Lesser General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//any later version.

//Plain is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU Lesser General Public License for more details.

//You should have received a copy of the GNU Lesser General Public License
//along with Plain.  If not, see <http://www.gnu.org/licenses/>.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI.WebControls;
using Plain;
using Plain.Data;
using Plain.Web.Utilities;
using Plain.Web.Utilities.FormBinders;
using Plain.Configuration;
using Plain.Validation;
using Plain.Web.Configuration;
using log4net;
using System.Web.UI;


namespace Plain.Web{
	public abstract class PageBaseEdit<T,TT> :PageBase where T:IBusinessEntity<TT>,new(){
        ILog log = LogManager.GetLogger("Plain.Web.PageBaseEdit");
		private T entity;
        private IFormBinding FormBinder = PlainWebApplication.Config.FormBinder;
		/// <summary>
		/// Returns the Id the page is loaded with. Throws an Exception if id is not right type.
		/// </summary>
		protected virtual TT EntityId
		{
			get {
				if(String.IsNullOrEmpty(Request["id"])){
					return default(TT);
				}
				else{
                    log.Debug("Retrieving EntityId");
				    return (TT)Convert.ChangeType(Request["id"], typeof(TT), null);
				}
			}
		}

		protected override void OnLoad(EventArgs e){
            if (!IsPostBack){
                DataBind();
				base.OnLoad(e);
				FormBinder.BindObjectToPage(Entity, this);			
			}
            Button Save = (Button)FindSave();//this.FindControl("Save") as Button;
			if (Save != null){
                log.Debug("Save button found. Hooking it up");
				Save.Click += new EventHandler(DoSave);
			}
		}

		protected override void OnLoadComplete(EventArgs e){
			base.OnLoadComplete(e);
            //calculate checksum of client data and cache checksum in viewstate
			ViewState["checksumLoad"] = ClientComparer.CalculateChecksum(this);
		}       
        
		protected T Entity {
			get {
				if (entity == null) {
                    log.Debug("Loading entity");
					entity = Dao.Get(EntityId);
				}
				return entity; 
			}
		}
        /// <summary>
        /// Return the <see cref="IDao"/> that matches <typeparam name="T"/> and <typeparam name="TT"/>
        /// </summary>
		protected virtual IDao<T, TT> Dao { 
			get { return DaoFactory.GetDao<T, TT>(); }
		 }

        /// <summary>
        /// Returns the <see cref="IDaoFactory"/> used by the <see cref="Dao"/> property to get Dao objects
         /// </summary>
		protected virtual IDaoFactory DaoFactory{
            get { return PlainWebApplication.Config.DaoFactory; }
	}
		//protected abstract IValueItemDao<T, TT> Dao { get; }
		public bool IsChanged()
		{
			string checksumLoad = (string)ViewState["checksumLoad"];
			string newChecksum = ClientComparer.CalculateChecksum(this);
			if(newChecksum != checksumLoad)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
        /// <summary>
        /// Performs a full save circle. If you want to hook into specific part of the logic, implement <see cref="OnBeforeSave"/> or <see cref="OnAfterSave"/>
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
		public void DoSave(object sender, EventArgs e) {
            Label lbl = this.FindRecursiveControl("Validations") as Label;
            if (lbl != null) {
                lbl.Text = "";
            }
			if (Entity == null) {
				entity = new T();
			}
			//Map data from UI to domain
			FormBinder.BindPageToObject(Entity, this);			
			
			//persist object
			OnBeforeSave(Entity);
            IList<string> result = new List<string>();
            if (!Validator.Validate(Entity, result)) { 
                string[] s= new string[result.Count];
                result.CopyTo(s, 0);
                if (lbl != null){
                    lbl.Text = String.Join("<br/>", s);
                    return;
                }else {
                    throw new Exception("Validation exception:\n" + String.Join("\n", s));
                }
            }
			Dao.Save(Entity);
			OnAfterSave(Entity);
			if (!entity.Id.Equals(EntityId)) {
				Response.Redirect(Request.RawUrl + "?id=" + entity.Id);
			}
		}
		/// <summary>
		/// Called before pageLoad.
		/// </summary>
		/// <param name="t"></param>
		protected virtual void OnBeforeLoad(T t) { }
        /// <summary>
        /// Called before save
        /// </summary>
        /// <param name="t"></param>
		protected virtual void OnBeforeSave(T t) { }
        /// <summary>
        /// called after save
        /// </summary>
        /// <param name="t"></param>
		protected virtual void OnAfterSave(T t) { }

        private Control FindSave()
        {
            foreach (Control con in Form.Controls)
            {
                if (con.ID == "Save")
                {
                    return con;
                }
                if (con.Controls.Count > 0)
                {
                    return FindSave(con.Controls);
                }
            }
            return null;
        }

        private Control FindSave(ControlCollection controls)
        {
            foreach (Control con in controls)
            {
                if (con.ID == "Save")
                {
                    return con;
                }
                if (con.Controls.Count > 0)
                {
                    return FindSave(con.Controls);
                }
            }
            return null;
        }

        protected Control FindRecursiveControl(string controlName) {
            foreach (Control con in Form.Controls) {
                if (con.ID == controlName) {
                    return con;
                }
                if (con.Controls.Count > 0) {
                    Control c = FindRecursiveControl(con.Controls,controlName);
                    if (c != null) {
                        return c;
                    }
                }
            }
            return null;
        }

        private Control FindRecursiveControl(ControlCollection controls, string controlName) {
            foreach (Control con in controls) {
                if (con.ID == controlName) {
                    return con;
                }
                if (con.Controls.Count > 0) {
                    Control c = FindRecursiveControl(con.Controls,controlName);
                    if(c!=null){
                        return c;
                    }
                }
            }
            return null;
        }
		
        
	}
}
